﻿/*********************************************************************************
**                                                                              **
**  Copyright (C) 2021-2025 LiLong                                              **
**  This file is part of CommonUnits.                                           **
**                                                                              **
**  CommonUnits is free software: you can redistribute it and/or modify         **
**  it under the terms of the GNU Lesser General Public License as published by **
**  the Free Software Foundation, either version 3 of the License, or           **
**  (at your option) any later version.                                         **
**                                                                              **
**  CommonUnits is distributed in the hope that it will be useful,              **
**  but WITHOUT ANY WARRANTY; without even the implied warranty of              **
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               **
**  GNU Lesser General Public License for more details.                         **
**                                                                              **
**  You should have received a copy of the GNU Lesser General Public License    **
**  along with CommonUnits.  If not, see <https://www.gnu.org/licenses/>.       **
**********************************************************************************/
#pragma once

#include "BasicFixedString.h"
#include "TypeTraits.h"

#include <ratio>
#include <tuple>
#include <type_traits>

namespace CU
{
template<typename T>
constexpr bool _IsRatioHelper = std::false_type {};

template<intmax_t Nx, intmax_t Den>
constexpr bool _IsRatioHelper<std::ratio<Nx, Den>> = std::true_type {};

template<typename Ratio>
concept RatioType = _IsRatioHelper<Ratio>;

template<BasicFixedString _unit, RatioType _Ratio = std::ratio<1>>
struct StandardUnit
{
    using String               = BasicFixedString;
    using BaseUnit             = StandardUnit<_unit>;
    using Ratio                = _Ratio;
    static constexpr auto unit = _unit;
};

template<typename T>
constexpr bool _isStandardUnitHelper = std::false_type {};

template<BasicFixedString unit, RatioType Ratio>
constexpr bool _isStandardUnitHelper<StandardUnit<unit, Ratio>> = std::true_type {};

template<typename _StandardUnit>
concept StandardUnitType = _isStandardUnitHelper<_StandardUnit>;

template<StandardUnitType _StandardUnit, BasicFixedString _unit, RatioType _Ratio>
struct DeriveUnit
{
    using String   = BasicFixedString;
    using BaseUnit = _StandardUnit;
    using Ratio    = _Ratio;
    static constexpr auto unit { _unit };
};

template<typename T>
constexpr bool _IsDeriveUnitHelper = std::false_type {};

template<StandardUnitType _StandardUnit, BasicFixedString unit, RatioType Ratio>
constexpr bool _IsDeriveUnitHelper<DeriveUnit<_StandardUnit, unit, Ratio>> = std::true_type {};

template<typename _DeriveUnit>
concept DeriveUnitType = _IsDeriveUnitHelper<_DeriveUnit>;

template<typename Unit>
concept UnitType = DeriveUnitType<Unit> || StandardUnitType<Unit>;

template<UnitType T, UnitType U>
constexpr bool _IsSeriesUnitHelper = std::is_same_v<typename T::BaseUnit, typename U::BaseUnit>;

template<typename T, typename U>
concept isSeriesUnit = _IsSeriesUnitHelper<std::decay_t<T>, std::decay_t<U>>;
} // namespace CU
